﻿using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Bonobo.Irc.Tests
{
    [TestClass]
    public sealed class IrcMessageTest
    {
        #region Constructor tests

        [TestMethod]
        [ExpectedException(typeof(ArgumentException))]
        public void IrcMsg_CtorEmptyCommand()
        {
            new IrcMessage("", "");
        }

        [TestMethod]
        [ExpectedException(typeof(ArgumentException))]
        public void IrcMsg_CtorNullCommand()
        {
            new IrcMessage(null, null);
        }

        [TestMethod]
        [ExpectedException(typeof(ArgumentException))]
        public void IrcMsg_CtorEmptyArg()
        {
            new IrcMessage("prefix", "PING", "");
        }

        [TestMethod]
        [ExpectedException(typeof(ArgumentException))]
        public void IrcMsg_CtorNullArg()
        {
            new IrcMessage("prefix", "PING", new String[] { null });
        }

        [TestMethod]
        [ExpectedException(typeof(ArgumentException))]
        public void IrcMsg_CtorTwoArgsWithSpaces()
        {
            new IrcMessage(null, "PING", "has spaces", "also has spaces");
        }

        [TestMethod]
        [ExpectedException(typeof(InvalidOperationException))]
        public void IrcMsg_CtorTooManyArgs()
        {
            new IrcMessage(null, "COMMAND", "1", "2", "3", "4", "5", "6", "7",
                "8", "9", "10", "11", "12", "13", "14", "15", "16");
        }

        [TestMethod]
        [ExpectedException(typeof(ArgumentException))]
        public void IrcMsg_AddSecondArgWithSpace()
        {
            var msg = new IrcMessage(null, "PING", "has_no_spaces", "has spaces");
            msg.Arguments.Add("a few more spaces that should be rejected");
        }

        [TestMethod]
        [ExpectedException(typeof(ArgumentException))]
        public void IrcMsg_SetFirstArgSpace()
        {
            var msg = new IrcMessage(null, "PING", "has_no_spaces", "also_no_spaces");
            msg.Arguments[0] = "an argument with spaces, should not be allowed";
        }
        
        #endregion

        #region ToString tests

        [TestMethod]
        public void IrcMsg_ToStringNoPrefixNoArgs()
        {
            var expected = "PING\r\n";
            
            var actual = new IrcMessage(null, "PING", null);
            Assert.AreEqual(expected, actual.ToString());

            actual = new IrcMessage("", "PING", new String[0]);
            Assert.AreEqual(expected, actual.ToString());
        }

        [TestMethod]
        public void IrcMsg_ToStringWithPrefixNoArgs()
        {
            var expected = ":prefix PONG\r\n";
            var actual = new IrcMessage("prefix", "PONG", null);

            Assert.AreEqual(expected, actual.ToString());
        }

        [TestMethod]
        public void IrcMsg_ToStringArgWithSpace()
        {
            var expected = ":prefix PRIVMSG blabla :string with a lot of spaces\r\n";
            var actual = new IrcMessage("prefix", "PRIVMSG", "blabla", "string with a lot of spaces");

            Assert.AreEqual(expected, actual.ToString());
        }

        #endregion

        #region Parsing test

        [TestMethod]
        public void IrcMsg_ParseNullString()
        {
            IrcMessage msg;
            Assert.IsFalse(IrcMessage.TryParse(null, out msg));
        }

        [TestMethod]
        public void IrcMsg_ParseEmptyString()
        {
            IrcMessage msg;
            Assert.IsFalse(IrcMessage.TryParse("", out msg));
        }

        [TestMethod]
        public void IrcMsg_ParseNoNewline()
        {
            IrcMessage msg;
            Assert.IsFalse(IrcMessage.TryParse("PING", out msg));
        }

        [TestMethod]
        public void IrcMsg_ParseNoPrefixNoArgs()
        {
            IrcMessage msg;
            Assert.IsTrue(IrcMessage.TryParse("PING\r\n", out msg));
            Assert.AreEqual("PING", msg.Command);
            Assert.AreEqual(null, msg.Prefix);
        }

        [TestMethod]
        public void IrcMsg_ParseWithPrefixNoArgs()
        {
            IrcMessage msg;
            Assert.IsTrue(IrcMessage.TryParse(":nick!user@hostname PONG\r\n", out msg));
            Assert.AreEqual("PONG", msg.Command);
            Assert.AreEqual("nick!user@hostname", msg.Prefix);
        }

        [TestMethod]
        public void IrcMsg_ParseWithPrefixSimpleArgs()
        {
            IrcMessage msg;
            Assert.IsTrue(IrcMessage.TryParse(":nick!user@hostname COMMAND arg1 arg2\r\n", out msg));
            Assert.AreEqual("COMMAND", msg.Command);
            Assert.AreEqual("nick!user@hostname", msg.Prefix);
            
            Assert.AreEqual(2, msg.Arguments.Count);
            Assert.AreEqual("arg1", msg.Arguments[0]);
            Assert.AreEqual("arg2", msg.Arguments[1]);
        }

        [TestMethod]
        public void IrcMsg_ParseWithPrefixSpaceArgs()
        {
            IrcMessage msg;
            Assert.IsTrue(IrcMessage.TryParse(":nick!user@hostname COMMAND arg1 :arg2 arg2 arg2 arg2\r\n", out msg));
            Assert.AreEqual("COMMAND", msg.Command);
            Assert.AreEqual("nick!user@hostname", msg.Prefix);

            Assert.AreEqual(2, msg.Arguments.Count);
            Assert.AreEqual("arg1", msg.Arguments[0]);
            Assert.AreEqual("arg2 arg2 arg2 arg2", msg.Arguments[1]);
        }

        [TestMethod]
        public void IrcMsg_ParseNoPrefixSingleArgWithSpace()
        {
            IrcMessage msg;
            Assert.IsTrue(IrcMessage.TryParse("COMMAND :arg1 arg2 arg2 arg2 arg2\r\n", out msg));
            Assert.AreEqual("COMMAND", msg.Command);
            Assert.AreEqual(null, msg.Prefix);

            Assert.AreEqual(1, msg.Arguments.Count);
            Assert.AreEqual("arg1 arg2 arg2 arg2 arg2", msg.Arguments[0]);
        }

        [TestMethod]
        public void IrcMsg_ParseNoPrefixSpaceArgsMalformed()
        {
            IrcMessage msg;
            Assert.IsTrue(IrcMessage.TryParse("COMMAND arg1 :\r\n", out msg));
            Assert.AreEqual("COMMAND", msg.Command);
            Assert.AreEqual(null, msg.Prefix);

            Assert.AreEqual(1, msg.Arguments.Count);
            Assert.AreEqual("arg1", msg.Arguments[0]);
        }

        [TestMethod]
        public void IrcMsg_ParseTooManyArgs()
        {
            IrcMessage msg;
            Assert.IsFalse(IrcMessage.TryParse("COMMAND 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16\r\n", out msg));
        }

        [TestMethod]
        public void IrcMsg_ParseNumericReplyTooLong()
        {
            IrcMessage msg;
            Assert.IsFalse(IrcMessage.TryParse("123456\r\n", out msg));
        }

        [TestMethod]
        public void IrcMsg_ParseNumericReplyTooShort()
        {
            IrcMessage msg;
            Assert.IsFalse(IrcMessage.TryParse("1\r\n", out msg));
        }

        [TestMethod]
        public void IrcMsg_ParseNumericReply()
        {
            IrcMessage msg;
            Assert.IsTrue(IrcMessage.TryParse("123\r\n", out msg));
        }

        [TestMethod]
        public void IrcMsg_ParseInvalidCommand()
        {
            IrcMessage msg;
            Assert.IsFalse(IrcMessage.TryParse("ABC123\r\n", out msg));
        }


        #endregion
        
        #region AsReadOnly

        [TestMethod]
        [ExpectedException(typeof(InvalidOperationException))]
        public void IrcMsg_ReadOnlySetCommand()
        {
            var msg = new IrcMessage("prefix", "command", "abc", "def").AsReadOnly();
            msg.Command = "another_command";
        }

        [TestMethod]
        [ExpectedException(typeof(InvalidOperationException))]
        public void IrcMsg_ReadOnlySetPrefix()
        {
            var msg = new IrcMessage("prefix", "command", "abc", "def").AsReadOnly();
            msg.Prefix = null;
        }

        [TestMethod]
        [ExpectedException(typeof(InvalidOperationException))]
        public void IrcMsg_ReadOnlyAddArg()
        {
            var msg = new IrcMessage("prefix", "command", "abc", "def").AsReadOnly();
            msg.Arguments.Add("xyz");
        }

        [TestMethod]
        [ExpectedException(typeof(InvalidOperationException))]
        public void IrcMsg_ReadOnlyRemoveArg()
        {
            var msg = new IrcMessage("prefix", "command", "abc", "def").AsReadOnly();
            msg.Arguments.RemoveAt(0);
        }

        [TestMethod]
        [ExpectedException(typeof(InvalidOperationException))]
        public void IrcMsg_ReadOnlySetArg()
        {
            var msg = new IrcMessage("prefix", "command", "abc", "def").AsReadOnly();
            msg.Arguments[0] = "1234";
        }

        [TestMethod]
        [ExpectedException(typeof(InvalidOperationException))]
        public void IrcMsg_ReadOnlyClearArgs()
        {
            var msg = new IrcMessage("prefix", "command", "abc", "def").AsReadOnly();
            msg.Arguments.Clear();
        }

        #endregion
    }
}
